/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Haystack Software Inc. All rights reserved.
 *  Licensed under the PolyForm Strict License 1.0.0. See License.txt in the project root for
 *  license information.
 *--------------------------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See code-license.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

"use strict"

//@ts-check
;(function () {
  /**
   * @returns {{mark(name:string):void, getMarks():{name:string, startTime:number}[]}}
   */
  function _definePolyfillMarks(timeOrigin) {
    const _data = []
    if (typeof timeOrigin === "number") {
      _data.push("code/timeOrigin", timeOrigin)
    }

    function mark(name) {
      _data.push(name, Date.now())
    }
    function getMarks() {
      const result = []
      for (let i = 0; i < _data.length; i += 2) {
        result.push({
          name: _data[i],
          startTime: _data[i + 1],
        })
      }
      return result
    }
    return { mark, getMarks }
  }

  /**
   * @returns {{mark(name:string):void, getMarks():{name:string, startTime:number}[]}}
   */
  function _define() {
    // Identify browser environment when following property is not present
    // https://nodejs.org/dist/latest-v16.x/docs/api/perf_hooks.html#performancenodetiming
    if (
      typeof performance === "object" &&
      typeof performance.mark === "function" &&
      !performance.nodeTiming
    ) {
      // in a browser context, reuse performance-util

      if (typeof performance.timeOrigin !== "number" && !performance.timing) {
        // safari & webworker: because there is no timeOrigin and no workaround
        // we use the `Date.now`-based polyfill.
        return _definePolyfillMarks()
      } else {
        // use "native" performance for mark and getMarks
        return {
          mark(name) {
            performance.mark(name)
          },
          getMarks() {
            let timeOrigin = performance.timeOrigin
            if (typeof timeOrigin !== "number") {
              // safari: there is no timerOrigin but in renderers there is the timing-property
              // see https://bugs.webkit.org/show_bug.cgi?id=174862
              timeOrigin =
                performance.timing.navigationStart ||
                performance.timing.redirectStart ||
                performance.timing.fetchStart
            }
            const result = [
              { name: "code/timeOrigin", startTime: Math.round(timeOrigin) },
            ]
            for (const entry of performance.getEntriesByType("mark")) {
              result.push({
                name: entry.name,
                startTime: Math.round(timeOrigin + entry.startTime),
              })
            }
            return result
          },
        }
      }
    } else if (typeof process === "object") {
      // node.js: use the normal polyfill but add the timeOrigin
      // from the node perf_hooks API as very first mark
      const timeOrigin =
        performance?.timeOrigin ??
        Math.round(
          (require.__$__nodeRequire || require)("perf_hooks").performance
            .timeOrigin,
        )
      return _definePolyfillMarks(timeOrigin)
    } else {
      // unknown environment
      console.trace("perf-util loaded in UNKNOWN environment")
      return _definePolyfillMarks()
    }
  }

  function _factory(sharedObj) {
    if (!sharedObj.MonacoPerformanceMarks) {
      sharedObj.MonacoPerformanceMarks = _define()
    }
    return sharedObj.MonacoPerformanceMarks
  }

  // This module can be loaded in an amd and commonjs-context.
  // Because we want both instances to use the same perf-data
  // we store them globally

  // eslint-disable-next-line no-var
  var sharedObj
  if (typeof global === "object") {
    // nodejs
    sharedObj = global
  } else if (typeof self === "object") {
    // browser
    sharedObj = self
  } else {
    sharedObj = {}
  }

  if (typeof define === "function") {
    // amd
    define([], function () {
      return _factory(sharedObj)
    })
  } else if (typeof module === "object" && typeof module.exports === "object") {
    // commonjs
    module.exports = _factory(sharedObj)
  } else {
    console.trace(
      "perf-util defined in UNKNOWN context (neither requirejs or commonjs)",
    )
    sharedObj.perf = _factory(sharedObj)
  }
})()
